/********************************************************************************
  * @file    projectstructuretab.cpp
  * @author  Lun Li
  * @version V1.5.0
  * @date    2022.4.10
  * @brief   This file provides all the ProjectStructureTab functions.
  ******************************************************************************/

#include "projectstructuretab.h"

#include <QAction>
#include <QApplication>
#include <QClipboard>
#include <QHeaderView>
#include <QLabel>

ProjectStructureTab::ProjectStructureTab(QWidget *parent)
    : QWidget(parent)
{
    initializeGUI();
    variableMenu = new QMenu(this);
    actionList << variableMenu->addAction(tr("copy name"), this, &ProjectStructureTab::on_copyVariableNameClicked)
               << variableMenu->addAction(tr("copy name with hierarchy"), this, &ProjectStructureTab::on_copyvariableHierarchyClicked)
               << variableMenu->addAction(tr("add variable to waveform"), this, &ProjectStructureTab::on_addVariableToWaveformClicked);

    connect(topLevelName, &QComboBox::currentIndexChanged, this, &ProjectStructureTab::on_topLevelChanged);
    connect(hierarchyView, &QTreeView::clicked, this, &ProjectStructureTab::on_hierarchyViewClicked);
    connect(hierarchyView, &QTreeView::doubleClicked, this, &ProjectStructureTab::on_hierarchyViewDoubleClicked);
    connect(searchVariable, &QLineEdit::textChanged, this, &ProjectStructureTab::on_searchStringChanged);
    connect(variableListView, &QListView::customContextMenuRequested, this, &ProjectStructureTab::on_variableMenuRequested);
}

void ProjectStructureTab::addModel(HierarchyModel *model)
{
    if (!model) {
        return;
    }
    int count = modelList.count();
    for (int i = 0; i < count; ++i) {
        if (modelList[i]->name() == model->name()) {
            modelList[i]->deleteLater();
            modelList[i] = model;
            topLevelName->setCurrentIndex(i);
            return;
        }
    }
    modelList.append(model);
    topLevelName->addItem(model->name());
    topLevelName->setCurrentIndex(topLevelName->count() - 1);
}

void ProjectStructureTab::removeModel(HierarchyModel *model)
{
    if (!model) {
        return;
    }
    int index = modelList.indexOf(model);
    if (index < 0) {
        return;
    }
    modelList.remove(index);
    topLevelName->removeItem(index);
    model->deleteLater();
    model = nullptr;
}

void ProjectStructureTab::initializeGUI()
{
    setLayout(new NoMarginVBoxLayout);
    QLabel *labelTopLevel = new QLabel(tr("Select Top Level:"), this);
    hierarchyView = new QTreeView(this);
    topLevelName = new QComboBox(this);
    QLabel *labelVariable = new QLabel(tr("Variables:"), this);
    searchVariable = new QLineEdit(this);
    variableListView = new QListView(this);

    hierarchyView->setEditTriggers(QTreeView::NoEditTriggers);
    hierarchyView->setHeaderHidden(true);
    hierarchyView->setHorizontalScrollMode(QTreeView::ScrollPerPixel);
    hierarchyView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
    hierarchyView->header()->setStretchLastSection(true);
    variableListView->setContextMenuPolicy(Qt::CustomContextMenu);
    variableListView->setEditTriggers(QTreeView::NoEditTriggers);

    layout()->addWidget(labelTopLevel);
    layout()->addWidget(topLevelName);
    layout()->addWidget(hierarchyView);
    layout()->addWidget(labelVariable);
    layout()->addWidget(searchVariable);
    layout()->addWidget(variableListView);
}

void ProjectStructureTab::on_topLevelChanged(int index)
{    
    if (hierarchyView->selectionModel()) {
        hierarchyView->selectionModel()->deleteLater();
    }
    if (index < 0) {
        hierarchyView->setModel(nullptr);
        return;
    }
    hierarchyView->setModel(modelList.at(index));
    hierarchyView->expandAll();
}

void ProjectStructureTab::on_searchStringChanged(const QString &text)
{
    if (!variableModel) {
        return;
    }
    if (text.isEmpty()) {
        variableListView->setModel(variableModel);
    } else {
        filterVariableModel->clear();
        QList<QPair<QString, QString>> variableList;
        int rowCount = variableModel->rowCount();
        for (int i = 0; i < rowCount; ++i) {
            variableList << QPair<QString, QString>(variableModel->item(i)->data(Qt::DisplayRole).toString(),
                                                    variableModel->item(i)->data(Qt::UserRole).toString());
        }
        for (const QPair<QString, QString> &pair : variableList) {
            QStandardItem *item = new QStandardItem(pair.first);
            item->setData(pair.first, Qt::DisplayRole);
            item->setData(pair.second, Qt::UserRole);
            filterVariableModel->appendRow(item);
        }
        variableListView->setModel(filterVariableModel);
    }
}

void ProjectStructureTab::on_hierarchyViewClicked(const QModelIndex &index)
{
    HierarchyItem *item = modelList.at(topLevelName->currentIndex())->itemFromIndex(index);
    variableListView->setModel(item->variableModel());
    variableModel = item->variableModel();
    on_searchStringChanged(searchVariable->text());
    curHierarchyItem = item;
    curHierarchyModel = modelList.at(topLevelName->currentIndex());
}

void ProjectStructureTab::on_hierarchyViewDoubleClicked(const QModelIndex &index)
{
    emit hierarchyItemDoubleClicked(modelList.at(topLevelName->currentIndex())->itemFromIndex(index)->file());
}

void ProjectStructureTab::on_variableMenuRequested(const QPoint &pos)
{
    QModelIndex index = variableListView->indexAt(pos);
    if (!index.isValid()) {
        return;
    }
    variableListView->selectionModel()->select(index, QItemSelectionModel::SelectCurrent);
    actionList[AddVariableToWaveform]->setEnabled(curHierarchyModel->file()->projectItem()->isWaveOpening());
    variableMenu->exec(variableListView->mapToGlobal(pos));
}

void ProjectStructureTab::on_copyVariableNameClicked()
{
    QStandardItem *item;
    if (searchVariable->text().isEmpty()) {
        item = variableModel->itemFromIndex(variableListView->currentIndex());
    } else {
        item = filterVariableModel->itemFromIndex(variableListView->currentIndex());
    }
    QApplication::clipboard()->setText(item->data(Qt::UserRole).toString());
}

void ProjectStructureTab::on_copyvariableHierarchyClicked()
{
    QStandardItem *item;
    if (searchVariable->text().isEmpty()) {
        item = variableModel->itemFromIndex(variableListView->currentIndex());
    } else {
        item = filterVariableModel->itemFromIndex(variableListView->currentIndex());
    }
    QString name = item->data(Qt::UserRole).toString();
    HierarchyItem *hierItem = curHierarchyItem;
    while (hierItem) {
        name.insert(0, hierItem->instanceName());
        hierItem = hierItem->parent();
    }
    QApplication::clipboard()->setText(name);
}

void ProjectStructureTab::on_addVariableToWaveformClicked()
{
    QStandardItem *item;
    if (searchVariable->text().isEmpty()) {
        item = variableModel->itemFromIndex(variableListView->currentIndex());
    } else {
        item = filterVariableModel->itemFromIndex(variableListView->currentIndex());
    }
    QString name = item->data(Qt::UserRole).toString();
    HierarchyItem *hierItem = curHierarchyItem;
    while (hierItem) {
        name.insert(0, hierItem->instanceName() + '.');
        hierItem = hierItem->parent();
    }
    emit addingVariableRequested(name);
}
